#include "nr_device_type_manager.h"
#include "nr_util.h"
#include <QtXml/QDomDocument>

#define NR_DEVICE_TYPE_FILE "\\DeviceSchema.xml"

nr_device_type_manager::nr_device_type_manager(nr_io_manager &io_manager)
    : m_is_loaded(false)
    , m_io_manager(io_manager)
{
}

nr_device_type_manager::~nr_device_type_manager()
{

}

static bool item_node_parse(QDomElement &item_node, nr_device_type_property &item_device_type_property)
{
    QDomNamedNodeMap attrs = item_node.attributes();//parameter_attrs

    Q_ASSERT(attrs.contains("id"));
    item_device_type_property.id() = attrs.namedItem("id").nodeValue().toInt();

    Q_ASSERT(attrs.contains("name"));
    item_device_type_property.name() = attrs.namedItem("name").nodeValue();

    Q_ASSERT(attrs.contains("text"));
    item_device_type_property.text() = attrs.namedItem("text").nodeValue();

    if(attrs.contains("category"))
    {
        item_device_type_property.category() = attrs.namedItem("category").nodeValue();
    }

    if(attrs.contains("description"))
    {
        item_device_type_property.description() = attrs.namedItem("description").nodeValue();
    }

    if ((attrs.contains("default")))
    {
        //QVariant
        QString str = attrs.namedItem("default").nodeValue();
        item_device_type_property.default_value() = attrs.namedItem("default").nodeValue();
    }

    if (attrs.contains("value"))
    {
        QString str = attrs.namedItem("value").nodeValue();
        item_device_type_property.value() = attrs.namedItem("value").nodeValue();
    }

    if ((attrs.contains("type")))
    {
        // Parse the type
        QString str_type = attrs.namedItem("type").nodeValue();
        item_device_type_property.property_type() = nr_device_type_property::parse_property_type(str_type);
    }
	
    if ((attrs.contains("value_type")))
    {
        // Parse the value_type
        QString str_type = attrs.namedItem("value_type").nodeValue();
        item_device_type_property.value_type() = nr_object::parser_value_type(str_type);
    }

    if ((attrs.contains("extra_data")))
    {
        // Parse the extra_data in Parameters item
        item_device_type_property.extra_data() = attrs.namedItem("extra_data").nodeValue();
    }

    return true;
}

//deal with item node by recursively,item maybe contain items
static bool items_node_to_property(QDomElement &items_node, nr_device_type_properties &item_device_type_propertys)
{
    QDomNodeList items_list = items_node.childNodes();//Item tags
    int items_list_count = items_list.count();
    for (int i = 0 ; i < items_list.count() ;i++)
    {
        Q_ASSERT( items_list.item(i).toElement().tagName()=="Item" );
        nr_device_type_property *device_type_item = new nr_device_type_property;
        item_node_parse(items_list.item(i).toElement(), *device_type_item);
        // Recursion
        items_node_to_property(items_list.item(i).toElement(), device_type_item->children());
        item_device_type_propertys.insert(device_type_item->id(), device_type_item);
    }
    return true;
}

static bool node_to_device_type(QDomElement &type_node, nr_device_type &device_type)
{
    QDomNamedNodeMap attrs = type_node.attributes();

    // Parse the device type attribute
    Q_ASSERT(attrs.contains("id"));
    device_type.id() = attrs.namedItem("id").nodeValue().toInt();

    Q_ASSERT(attrs.contains("name"));
    device_type.name() = attrs.namedItem("name").nodeValue();

    Q_ASSERT(attrs.contains("text"));
    device_type.text() = attrs.namedItem("text").nodeValue();
    
    Q_ASSERT(attrs.contains("category"));
    device_type.category() = attrs.namedItem("category").nodeValue();
    
    Q_ASSERT(attrs.contains("description"));
    device_type.description() = attrs.namedItem("description").nodeValue();

    Q_ASSERT(attrs.contains("description"));
    device_type.module() = attrs.namedItem("module").nodeValue();

    // Parse sub-device types
    QDomNodeList sub_types_node = type_node.elementsByTagName("SubDeviceType");
    for(int i = 0; i < sub_types_node.count(); ++i)
    {
        QDomElement sub_type_node = sub_types_node.item(i).toElement();
        Q_ASSERT(sub_type_node.hasAttribute("id"));
        
        int sub_type_id = sub_type_node.attribute("id").toInt();
        Q_ASSERT(!device_type.child_types().contains(sub_type_id));
        device_type.child_types().insert(sub_type_id);
    }

    // Parse the properties
    QDomNode properties_node = type_node.namedItem("Properties");
    if (!properties_node.isNull())
    {
        bool ret = items_node_to_property(properties_node.toElement(), device_type.properties());
        Q_ASSERT(ret);
    }

    // Parse the parameters
    QDomNode parameters_node = type_node.namedItem("Parameters");
    if ( !parameters_node.isNull() )
    {
        bool ret = items_node_to_property(parameters_node.toElement(), device_type.parameters());
        Q_ASSERT(ret);
    }
    
    // Parser the events
    QDomNode events_node = type_node.namedItem("Events");
    if (!events_node.isNull())
    {
        bool ret = items_node_to_property(events_node.toElement(), device_type.events());
        Q_ASSERT(ret);
    }

    return true;
}

nr_error_t nr_device_type_manager::load()
{
    if (m_is_loaded)
        return NR_ERROR_NONE;

    QString xml_path = nr_util::get_install_dir() + QString(NR_DEVICE_TYPE_FILE);

    QFile file(xml_path);
    if (!file.open(QIODevice::ReadOnly))
    {
        return NR_ERROR_OPEN_FILE_FAILED;
    }

    QString error_msg;
    int error_line = 0, error_column = 0; 
    QDomDocument doc;
    if (!doc.setContent(&file, &error_msg, &error_line, &error_column))
    {
        return NR_ERROR_PARSE_XML_FAILED;
    }
    
    QDomElement types_node = doc.namedItem("DeviceTypes").toElement();
    QDomNodeList type_list = types_node.elementsByTagName("DeviceType");

    // Parse each dom node
    for(int i = 0 ; i < type_list.count(); ++i)
    {
        nr_device_type *device_type = new nr_device_type;
        
        // Convert the device type node to device type
        bool ret = node_to_device_type(type_list.item(i).toElement(), *device_type);
        Q_ASSERT(ret);
        Q_ASSERT(!m_device_types.contains(device_type->id()));

        m_device_types.insert(device_type->id(), device_type);
    }
    
    // Load the module of each device type
    foreach(nr_device_type* device_type, m_device_types)
    {
        nr_error_t status = device_type->load_module();
        Q_ASSERT(NR_SUCCEED(status));
    }

    m_is_loaded = true;

    return NR_ERROR_NONE;
}

nr_error_t nr_device_type_manager::unload()
{
    if (!m_is_loaded)
        return NR_ERROR_NONE;

    foreach(nr_device_type* device_type, m_device_types)
    {
        delete device_type;
    }

    m_device_types.clear();

    return NR_ERROR_NONE;
}

static bool create_attributte_to_element(QDomDocument &doc, QDomElement &node, const QString &domattrname, QString &domattrvalue)
{
    QDomAttr domattr_para = doc.createAttribute(domattrname);
    domattr_para.setValue( domattrvalue );
    node.setAttributeNode( domattr_para );
    return true;
}

static bool create_item_node(QDomDocument &doc, QDomElement &item_node, nr_device_type_property &item_property)
{
    create_attributte_to_element(doc, item_node, "id", QString::number(item_property.id()) );
    create_attributte_to_element(doc, item_node, "name", QString(item_property.name()) );
    create_attributte_to_element(doc, item_node, "text", QString(item_property.text()) );
    if ( QString(item_property.category()) != "" )
        create_attributte_to_element(doc, item_node, "category", QString(item_property.category()) );
    if ( QString(item_property.description()) != "" )
        create_attributte_to_element(doc, item_node, "description", QString(item_property.description()) );
    if ( QString(item_property.value().toString()) != "" )
        create_attributte_to_element(doc, item_node, "value", QString(item_property.value().toString()) );
    if ( QString(item_property.default_value().toString()) != "" )
        create_attributte_to_element(doc, item_node, "default", QString(item_property.default_value().toString()) );
    //property_type
    if ( QString(item_property.toString_property_type()) != "" && item_property.property_type() != nr_device_type_property::property_type_t(0) )
        create_attributte_to_element(doc, item_node, "type", QString(item_property.toString_property_type()) );
    //value_type
    if ( QString(item_property.toString_value_type()) != "" && item_property.value_type() != nr_object::value_type_t(0))
        create_attributte_to_element(doc, item_node, "value_type", QString(item_property.toString_value_type()) );
    if ( QString(item_property.extra_data()) != "" )
        create_attributte_to_element(doc, item_node, "extra_data", QString(item_property.extra_data()) );
    return true;
}

static bool create_nodes_to_items(QDomDocument &doc, QDomElement &nodes, nr_device_type_properties &item_device_type_propertys)
{
    QHashIterator<nr_id_t, nr_device_type_property*> devicetypes_iterator( item_device_type_propertys );
    while ( devicetypes_iterator.hasNext() ) {
        devicetypes_iterator.next();
        //devicetypes_iterator.key();
        //devicetypes_iterator.value();

        QDomElement item_node = doc.createElement("Item");
        //createattributte_to_element( doc , item_node , *devicetypes_iterator.value() );
        create_item_node(doc, item_node, *devicetypes_iterator.value() );
        if ( !devicetypes_iterator.value()->children().isEmpty() )
        {
            create_nodes_to_items(doc, item_node, devicetypes_iterator.value()->children() );
        }
        nodes.appendChild(item_node);
    }

    return true;
}



static bool create_node_to_devicetypes(QDomDocument &doc, QDomElement &devicetype_node, nr_device_type &device_type)
{
    //create text of DeviceType
    create_attributte_to_element(doc, devicetype_node, "id", QString::number(device_type.id()) );
    create_attributte_to_element(doc, devicetype_node, "name", QString(device_type.name()) );
    create_attributte_to_element(doc, devicetype_node, "text", QString(device_type.text()) );
    create_attributte_to_element(doc, devicetype_node, "category", QString(device_type.category()) );
    create_attributte_to_element(doc, devicetype_node, "description", QString(device_type.description()) );
    if ( QString(device_type.module()) != "" ) {
        create_attributte_to_element(doc, devicetype_node, "module", QString(device_type.module()) );
    }

    //create SubDeviceType node to DeviceType
    QSet<nr_id_t>::iterator subdeviceid_iterator;
    subdeviceid_iterator = device_type.child_types().begin();
    for ( ; subdeviceid_iterator != device_type.child_types().end() ; ++subdeviceid_iterator )
    {
        QDomElement subdevicetype_node = doc.createElement("SubDeviceType");
        create_attributte_to_element(doc, subdevicetype_node, "id", QString::number(*subdeviceid_iterator) );
        devicetype_node.appendChild( subdevicetype_node );
    }

    //create Properties node to DeviceType
    if ( !device_type.properties().isEmpty() )
    {
        QDomElement properties_node = doc.createElement("Properties");
        //call recurrence
        create_nodes_to_items(doc, properties_node, device_type.properties() );
        devicetype_node.appendChild( properties_node );
    }

    //create Parameters node to DeviceType
    if ( !device_type.parameters().isEmpty() )
    {
        QDomElement parameters_node = doc.createElement("Parameters");
        //call recurrence
        create_nodes_to_items(doc, parameters_node, device_type.parameters() );
        devicetype_node.appendChild( parameters_node );
    }

    //create Events node to DeviceType
    if ( !device_type.events().isEmpty() )
    {
        QDomElement events_node = doc.createElement("Events");
        //call recurrence
        create_nodes_to_items(doc, events_node, device_type.events() );
        devicetype_node.appendChild( events_node );
    }

    return true;
}

nr_error_t nr_device_type_manager::save_as_xml(const QString& xml_file)
{
    QFile file(xml_file);
    if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
    {
        return NR_ERROR_OPEN_FILE_FAILED;
    }

    QDomDocument doc;
    QDomText text;
    QDomElement element;
    QDomProcessingInstruction instruction;
    instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"utf-8\"");
    doc.appendChild(instruction);

    QDomElement root = doc.createElement("DeviceTypes");
    doc.appendChild(root);

    QHashIterator<nr_id_t, nr_device_type*> devicetypes_iterator( this->m_device_types );
    while ( devicetypes_iterator.hasNext() ) {
        devicetypes_iterator.next();
        //devicetypes_iterator.key();//devicetypes_iterator.value();

        //add DeviceType one by one
        QDomElement devicetype_node = doc.createElement("DeviceType");
        create_node_to_devicetypes( doc , devicetype_node , *devicetypes_iterator.value() );

        root.appendChild(devicetype_node);
    }

    QTextStream out(&file);
    doc.save(out,4);
    return NR_ERROR_NONE;
}

nr_device_type* nr_device_type_manager::get_device_type(const nr_id_t &type_id)
{
    nr_device_types::iterator it = m_device_types.find(type_id);
    if (it != m_device_types.end())
    {
        return it.value();
    }
    else
    {
        Q_ASSERT(false);
        return NULL;
    }
}